/*
 * Copyright 2005 The Apache Software Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.subshell.lucene.indexaccess.impl;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.index.IndexDeletionPolicy;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.MultiSearcher;
import org.apache.lucene.search.Searchable;
import org.apache.lucene.search.Searcher;
import org.apache.lucene.search.Similarity;
import org.apache.lucene.store.Directory;

import com.subshell.lucene.indexaccess.IIndexAccessProvider;

/**
 * Default IIndexAccessProvider implementation.
 * 
 * @author Maik Schreiber (blizzy AT blizzy DOT de)
 */
public final class IndexAccessProvider implements IIndexAccessProvider {

    /** Logger. */
    private final Log log = LogFactory.getLog(IndexAccessProvider.class);

    /** Array of Searchable objects. */
    private static final Searchable[] EMPTY_SEARCHABLE_ARRAY = new Searchable[0];

    /** Index deletion policy. */
    private IndexDeletionPolicy deletionPolicy;

    /** The Lucene directory object. */
    private Directory directory;

    /** The Lucene analyser object. */
    private Analyzer analyzer;

    /** The debug information stream. */
    private PrintStream infoStream = null;

    /**
     * Constructs a new IndexAccessProvider.
     * 
     * @param directoryParam
     *            the underlying directory
     * @param analyzerParam
     *            the Analyser to be used for IndexWriters
     * @param deletionPolicyParam
     *            The deletion policy.
     */
    public IndexAccessProvider(final Directory directoryParam,
            final Analyzer analyzerParam,
            final IndexDeletionPolicy deletionPolicyParam) {
        this.directory = directoryParam;
        this.analyzer = analyzerParam;
        this.deletionPolicy = deletionPolicyParam;
    }

    /**
     * {@inheritDoc}
     */
    public IndexReader getReader() throws IOException {
        log.debug("getReader()");
        return IndexReader.open(directory, deletionPolicy);
    }

    /**
     * {@inheritDoc}
     */
    public IndexWriter getWriter() throws IOException {
        log.debug("getWriter()");
//        IndexWriter.setDefaultInfoStream(getInfoStream());
        return new IndexWriter(directory, false, analyzer, false,
                deletionPolicy);
    }

    /**
     * {@inheritDoc}
     */
    public Searcher getSearcher(final Similarity similarity,
            final Set underlyingSearchers) throws IOException {

        Searcher result;
        if ((underlyingSearchers == null) || underlyingSearchers.isEmpty()) {
            result = new IndexSearcher(directory);
        } else {
            result = new MultiSearcher((Searchable[]) underlyingSearchers
                    .toArray(EMPTY_SEARCHABLE_ARRAY));
        }
        result.setSimilarity(similarity);
        return result;
    }

    /**
     * {@inheritDoc}
     */
    public Directory getDirectory() {
        return directory;
    }

    /**
     * {@inheritDoc}
     */
    public void close(final IndexReader reader) {
        if (reader != null) {
            try {
                reader.close();
                log.debug("close(IndexReader)");
            } catch (IOException e) {
                log.error("close(IndexReader)", e);
                System.out.println("ERROR closing IndexReader:");
                e.printStackTrace(System.out);
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    public void close(final IndexWriter writer) {
        if (writer != null) {
            try {
                writer.close();
                closeInfoStream();
                log.debug("close(IndexWriter)");
            } catch (IOException e) {
                log.error("close(IndexWriter)", e);
                System.out.println("ERROR closing IndexWriter:");
                e.printStackTrace(System.out);
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    public void close(final Searcher searcher) {
        if (searcher != null) {
            try {
                searcher.close();
                log.debug("close(Searcher)");
            } catch (IOException e) {
                log.error("close(Searcher)", e);
                System.out.println("ERROR closing IndexSearcher:");
                e.printStackTrace(System.out);
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    public void close(final Directory directoryParam) {
        // nothing to do since we haven't opened it
    }

    /**
     * Create an information stream for the index writer.
     * 
     * @return A new print stream.
     * @throws FileNotFoundException
     *             If the file cannot be created.
     */
    private PrintStream getInfoStream() throws FileNotFoundException {
        log.debug("getInfoStream()");
        closeInfoStream();
        //infoStream = new PrintStream(new BufferedOutputStream(
        //new FileOutputStream(new File("index-writer-info-stream.out"),
        //true)));
        infoStream = System.out;
        //infoStream = null;
        return infoStream;
    }

    /**
     * Close the information stream.
     */
    private void closeInfoStream() {
        log.debug("closeInfoStream()");
        //if (infoStream != null) {
        //infoStream.close();
        //infoStream = null;
        //}
    }
}
